﻿using System;
using System.Collections.Generic;
using System.Collections;
using System.Text;
using System.Net.Sockets;
using System.Net;
using System.Diagnostics;
using System.IO;
using System.Runtime.Serialization.Formatters.Binary;

using FluorineFx.AMF3;
using FluorineFx;

namespace Game.Server.Socket
{
    /// <summary> 
    /// 通讯事件委托 
    /// </summary> 
    public delegate void SocketEvent(object sender, SocketEventArgs evt);

    public class SocketServer
    {
        /// <summary> 
        /// 数据缓冲区大小(64K)
        /// </summary>
        public int DefaultBufferSize = 64 * 1024;

        /// <summary> 
        /// Socket主机端口 
        /// </summary> 
        private ushort _port;

        /// <summary> 
        /// Socket主机允许的最大客户端连接会话数 
        /// </summary> 
        private int _maxSession;

        /// <summary> 
        /// 运行状态 
        /// </summary> 
        private bool _isRun;

        /// <summary> 
        /// 接收数据缓冲区
        /// </summary> 
        private byte[] _recvDataBuffer;

        /// <summary> 
        /// 服务器使用的异步Socket类, 
        /// </summary> 
        private System.Net.Sockets.Socket _serverSocket;

        /// <summary> 
        /// 保存所有客户端会话的哈希表 
        /// </summary> 
        private Hashtable _sessionTable;

        /// <summary> 
        /// 当前的连接的客户端会话数 
        /// </summary> 
        private ushort _sessionCount;

        /// <summary> 
        /// 客户端建立连接事件 
        /// </summary> 
        public event SocketEvent ClientConnectEvent;

        /// <summary> 
        /// 客户端关闭事件 
        /// </summary> 
        public event SocketEvent ClientCloseEvent;

        /// <summary> 
        /// 服务器已满事件 
        /// </summary> 
        public event SocketEvent ServerFullEvent;

        /// <summary> 
        /// 服务器接收到数据事件 
        /// </summary> 
        public event SocketEvent SocketDataEvent;

        /// <summary> 
        /// 构造函数
        /// </summary> 
        /// <param name="port">端口</param> 
        /// <param name="maxSession">最大客户端容纳数</param> 
        public SocketServer(ushort port, ushort maxSession)
        {
            _port = port;
            _maxSession = maxSession;
        }

        /// <summary> 
        /// 启动服务器程序,开始监听客户端请求 
        /// </summary> 
        public void Start()
        {
            if (_isRun)
            {
                throw (new ApplicationException("已经在运行."));
            }

            _sessionTable = new Hashtable();//创建在线客户端集
            _recvDataBuffer = new byte[DefaultBufferSize];
            _serverSocket = new System.Net.Sockets.Socket(AddressFamily.InterNetwork, SocketType.Stream, ProtocolType.Tcp);
            IPEndPoint ipEndPoint = new IPEndPoint(IPAddress.Any, _port);
            _serverSocket.Bind(ipEndPoint);

            _serverSocket.Listen(_maxSession);
            _serverSocket.BeginAccept(new AsyncCallback(AcceptConn), _serverSocket);//接收客户端连接 
            _isRun = true;//标识Socket服务器已运行
        }

        /// <summary> 
        /// 停止服务器程序及关闭所有已连接的客户端 
        /// </summary> 
        public void Stop()
        {
            if (!_isRun)
            {
                throw (new ApplicationException("已经停止"));
            }

            _isRun = false;

            //关闭数据连接
            if (_serverSocket.Connected)
            {
                _serverSocket.Shutdown(SocketShutdown.Both);
            }

            CloseAllClient();
            _serverSocket.Close();
            _serverSocket = null;
        }

        /// <summary> 
        /// 关闭所有客户端会话
        /// </summary> 
        public void CloseAllClient()
        {
            foreach (Session client in _sessionTable.Values)
            {
                client.Close();
            }

            _sessionTable.Clear();
        }


        /// <summary> 
        /// 关闭指定的客户端会话 
        /// </summary> 
        /// <param name="closeClient">客户端会话对象</param> 
        public void CloseSession(Session session)
        {
            if (session != null)
            {
                _sessionTable.Remove(session.ID);
                _sessionCount--;

                //客户端强制关闭链接 
                if (ClientCloseEvent != null)
                {
                    ClientCloseEvent(this, new SocketEventArgs(session));
                }

                session.Close();
            }
        }

        /// <summary> 
        /// 发送数据 
        /// </summary> 
        /// <param name="session">接收数据的客户端会话</param> 
        /// <param name="socketPack">数据包</param> 
        public void Send(Session session, SocketPack socketPack)
        {
            ByteArray lenByte = new ByteArray();
            lenByte.WriteObject(socketPack.PackData);
            byte[] lenBuf = new byte[lenByte.Length];
            lenByte.Position = 0;
            lenByte.ReadBytes(lenBuf, (uint)0, (uint)lenBuf.Length);
            lenByte.Compress();

            ByteArray objByte = new ByteArray();
            objByte.WriteInt(socketPack.DataTransferType);
            objByte.WriteInt(lenBuf.Length);
            objByte.WriteBytes(lenBuf, 0, lenBuf.Length);
            byte[] objBuf = new byte[objByte.Length];
            objByte.Position = 0;
            objByte.ReadBytes(objBuf, (uint)0, (uint)objBuf.Length);

            session.ClientSocket.BeginSend(objBuf, 0, objBuf.Length, SocketFlags.None, new AsyncCallback(SendDataEnd), session.ClientSocket);
        }

        /// <summary> 
        /// 关闭客户端Socket
        /// </summary> 
        /// <param name="client">客户端Socket对象</param> 
        /// <param name="exitType">退出类型</param> 
        private void CloseClient(System.Net.Sockets.Socket client, Session.ExitTypes exitType)
        {
            Session closeClient = FindSession(client);
            closeClient.ExitType = exitType;

            if (closeClient != null)
            {
                CloseSession(closeClient);
            }
            else
            {
                throw (new ApplicationException("所关闭的Socket对象不存在"));
            }
        }

        /// <summary> 
        /// 客户端连接处理
        /// </summary> 
        /// <param name="iar">Socket对象</param> 
        private void AcceptConn(IAsyncResult iar)
        {
            if (!_isRun)
            {
                return;
            }

            //接收一个客户端的连接请求 
            System.Net.Sockets.Socket oldserver = (System.Net.Sockets.Socket)iar.AsyncState;
            System.Net.Sockets.Socket client = oldserver.EndAccept(iar);

            if (_sessionCount >= _maxSession)
            {//如果已达到最大允许连接客户端数
                if (ServerFullEvent != null)
                {
                    ServerFullEvent(this, new SocketEventArgs(new Session(client)));//发出服务器已满事件 
                }
            }
            else
            {
                Session session = new Session(client);
                _sessionTable.Add(session.ID, session);

                //客户端计数+1 
                _sessionCount++;

                //开始接受来自该客户端的数据 
                client.BeginReceive(_recvDataBuffer, 0, _recvDataBuffer.Length, SocketFlags.None, new AsyncCallback(ReceiveData), client);

                //新的客户端连接,发出通知 
                if (ClientConnectEvent != null)
                {
                    ClientConnectEvent(this, new SocketEventArgs(session));
                }
            }

            //继续接收客户端 
            _serverSocket.BeginAccept(new AsyncCallback(AcceptConn), _serverSocket);
        }

        /// <summary> 
        /// 通过Socket对象查找Session对象 
        /// </summary> 
        /// <param name="client">客户端对象</param>
        private Session FindSession(System.Net.Sockets.Socket client)
        {
            return (Session)_sessionTable[(int)client.Handle];
        }

        /// <summary> 
        /// 接收数据完成处理
        /// </summary> 
        /// <param name="iar">客户端Socket</param> 
        private void ReceiveData(IAsyncResult iasyncResult)
        {
            System.Net.Sockets.Socket client = (System.Net.Sockets.Socket)iasyncResult.AsyncState;
            if (client == null || !client.Connected) return;

            try
            {
                int receiveLen = client.EndReceive(iasyncResult);
                if (receiveLen == 0)
                {
                    CloseClient(client, Session.ExitTypes.Normal);//关闭客户端
                    return;
                }

                string receivedData = Encoding.UTF8.GetString(_recvDataBuffer, 0, receiveLen);
                if (SocketDataEvent != null && receivedData != "<policy-file-request/>\0")
                {
                    Session sendDataSession = FindSession(client);
                    int ind = 0;
                    while (receiveLen > 0)
                    {
                        #region 读消息头
                        //取得数据传输类型
                        MemoryStream msTransferType = new MemoryStream(_recvDataBuffer, ind, 4);
                        ByteArray bytemsTransferType = new ByteArray(msTransferType);
                        int transferType = bytemsTransferType.ReadInt();
                        msTransferType.Dispose();
                        msTransferType.Close();

                        //取得数据包长度
                        MemoryStream msLen = new MemoryStream(_recvDataBuffer, ind + 4, 4);
                        ByteArray byteLen = new ByteArray(msLen);
                        Int32 len = byteLen.ReadInt();
                        msLen.Dispose();
                        msLen.Close();
                        #endregion

                        if (len == 0) { break; }

                        //根据头长度取内容
                        MemoryStream msData = new MemoryStream(_recvDataBuffer, ind + 8, len);
                        ByteArray baData = new ByteArray(msData);
                        baData.Uncompress();
                        ASObject packData = (ASObject)baData.ReadObject();
                        msData.Dispose();
                        msData.Close();

                        //BinaryFormatter binaryF = new BinaryFormatter();
                        //MemoryStream ms = new MemoryStream(_recvDataBuffer, ind + 8, len, false);
                        //object packData = binaryF.Deserialize(ms);
                        //ms.Close();

                        SocketPack socketPack = new SocketPack((int)packData["rootPackCode"], (int)packData["packCode"], (int)packData["dataTransferType"]);
                        socketPack.PackData = packData;

                        ind += (len + 4);
                        receiveLen -= (len + 4);

                        if (sendDataSession != null)
                        {
                            //发布收到消息事件
                            SocketDataEvent(this, new SocketEventArgs(sendDataSession, socketPack));
                        }
                    }
                    _recvDataBuffer.Initialize();
                }

                //继续接收客户端数据 
                client.BeginReceive(_recvDataBuffer, 0, _recvDataBuffer.Length, SocketFlags.None, new AsyncCallback(ReceiveData), client);
            }
            catch (SocketException ex)
            {
                //客户端退出 
                if (ex.ErrorCode == 10054)
                {
                    CloseClient(client, Session.ExitTypes.Exception);//关闭客户端
                }

            }
            catch (ObjectDisposedException ex)
            {
                if (ex != null)
                {
                    ex = null;
                }
            }

        }

        /// <summary> 
        /// 发送数据完成处理函数 
        /// </summary> 
        /// <param name="iar">客户端Socket</param> 
        private void SendDataEnd(IAsyncResult iar)
        {
            System.Net.Sockets.Socket client = (System.Net.Sockets.Socket)iar.AsyncState;

            if (client != null) client.EndSend(iar);
        }


        #region 属性
        /// <summary> 
        /// 服务器的Socket对象 
        /// </summary> 
        public System.Net.Sockets.Socket ServerSocket
        {
            get
            {
                return _serverSocket;
            }
        }

        /// <summary> 
        /// 客户端会话集
        /// </summary> 
        public Hashtable SessionTable
        {
            get
            {
                return _sessionTable;
            }
        }

        /// <summary> 
        /// 当前客户端连接总数 
        /// </summary> 
        public int SessionCount
        {
            get
            {
                return _sessionCount;
            }
        }

        /// <summary> 
        /// 服务器运行状态 
        /// </summary> 
        public bool IsRun
        {
            get
            {
                return _isRun;
            }

        }
        #endregion
    }
}
